package cz.spock.parser;

import java.util.Collections;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

import org.springframework.beans.factory.annotation.Autowired;

import cz.spock.parser.entity.RewriteRule;
import cz.spock.parser.entity.Rule;
import cz.spock.parser.entity.RuleSet;
import cz.spock.parser.entity.TagPattern;
import cz.spock.parser.entity.TripletPattern;
import cz.spock.parser.service.RuleSetService;
import cz.spock.parser.util.TripletPatternComparator;

/**
 * Main class of spock parser
 * 
 * @author miso
 *
 */
public class Parser implements ParserInterface {
    
    @Autowired
    private RuleSetService rsService;

    @Override
    @Deprecated // not implemented
    public String parse(String data, List<RuleSet> rules) {
        // check input data
        if(data == null) return null;
        if(rules == null || rules.isEmpty()) return data;
        // prepare list of rules: merge and sort by weight
        RuleSet rulez = null;
        if(rules.size() > 1) {
            // TODO implement 
        } else {
            rulez = rules.get(0);
        }
        return parse(data, rulez);
    }

    @Override
    public String parse(String data, RuleSet rules) {
        // check input
        if(data == null) return null;
        if(rules == null) return data;
        // prepare rules: sort by weight and prepare builder
        Collections.sort(rules.getRules(), new TripletPatternComparator());
        rsService.createTransientListOfTriplets(rules);
        StringBuilder builder = new StringBuilder(data);
        // parse & transform
            // main cycle
        for(TripletPattern pat : rules.getRules()) {
            // create regex
            String regex = pat.getStartTag().getRegex() + pat.getContent().getRegex() + pat.getEndTag().getRegex();
            if(regex == null || regex.isEmpty()) continue;
            Pattern p;
            // check regex syntax
            try {
                p = Pattern.compile(regex);
            } catch(PatternSyntaxException e) {
                continue;
            }
            // prepare matcher
            Matcher m = p.matcher(builder);
            StringBuffer result = new StringBuffer();
            
//            int position = 0;
//            int positionBefore = -1;
            // find all matches of regex and create result
            while(m.find()) {
//                position = m.start();
                // aware infinite loop TODO
//                if(positionBefore == position) {
//                    position++;
//                    continue;
//                }
//                positionBefore = position;
                
                // process match
                String temp = this.processTriplet(m.group(), pat);
                temp = temp.replaceAll("\\$", "");
                m.appendReplacement(result, );
                this.processTriplet(m.group(), pat);
            }
        }
        return null;
    }
    
    /**
     * applies TriplePattern rules on given string
     * 
     * returns null when string was not processed successfully
     * returns transformed string, pre and post substrings are omitted
     * 
     * @param data
     * @param pat
     * @return
     */
    public String processTriplet(String data, TripletPattern pat) {
        // check input data
        if(data == null || data.isEmpty()) return null;
        if(pat == null) return data;
        // process each part of triplet and append result
        StringBuilder transformed = new StringBuilder();
        transformed.append(parseAndTransformStrict(data, pat.getStartTag()));
        transformed.append(parseAndTransformStrict(data, pat.getContent()));
        transformed.append(parseAndTransformStrict(data, pat.getEndTag()));
        return transformed.toString();
    }
    
    /**
     * given string is transformed according to given pattern
     * 
     * string is matched against pattern regex only once, the rest of string is thrown away
     * 
     * method transforms and returns only matched part of given string! 
     * 
     * @param data
     * @param pattern
     * @return
     */
    public String parseAndTransformStrict(String data, TagPattern pattern) {
        try {
            Pattern pat = Pattern.compile(pattern.getRegex());
            Matcher matcher = pat.matcher(data);
            if(!matcher.find()) {                
                // if given pattern does not match data
                return data.toString();
            } else {
                String transformed = this.transformer(matcher.group(), pattern);
                return transformed;
            }            
        } catch(PatternSyntaxException e) {
            return null;
        }
    }
    
    /**
     * given string is transformed according to given pattern
     * 
     * string is matched against pattern regex only once, the unmatched parts of string are kept
     * 
     * @param data
     * @param pattern
     * @return
     */
    public String parseAndTransform(StringBuilder data, TagPattern pattern) {
        try {
            Pattern pat = Pattern.compile(pattern.getRegex());
            Matcher matcher = pat.matcher(data);
            if(!matcher.find()) {
                
            } else {
                String transformedStart = this.transformer(matcher.group(), pattern);
                data.delete(matcher.start(), matcher.end());
                data.insert(matcher.start(), transformedStart);
            }            
        } catch(PatternSyntaxException e) {
            return null;
        }
        return data.toString();        
    }
    
    /**
     * transforms given string according to given pattern rules
     * 
     * @param data
     * @param pattern
     * @return
     */
    public String transformer(String data, TagPattern pattern) {
        if(data == null) return null;
        if(pattern == null) return data;
        if(pattern.getRules() == null || pattern.getRules().isEmpty()) return data;
        for(Rule r : pattern.getRules()) {
            if(r instanceof RewriteRule) {
                data = this.applyRewriteRule(data, (RewriteRule) r);
            }
            // add another rules when available
            
        }
        return data;
    }
    
    /**
     * transforms given string according to given rewrite rule
     * 
     * @param data
     * @param rule
     * @return
     */
    public String applyRewriteRule(String data, RewriteRule rule) {
        data = rule.getRewrite();
        return data;
    }
    
        
}
